﻿namespace MetaProperties.Core
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using MetaProperties.Framework;

    /// <summary>
    /// Extension methods for the MetaProperties.Core library.
    /// </summary>
    public static class Extensions
    {
        /// <summary>
        /// Gets the synchronization context container.
        /// </summary>
        /// <param name="configuration">The configuration.</param>
        /// <returns>The synchronization context container.</returns>
        public static ISynchronizationContextContainer GetSynchronizationContextContainer(this MetaPropertyConfiguration configuration)
        {
            return GetSynchronizationContextContainerFromAttributes(configuration.Attributes);
        }

        /// <summary>
        /// Initializes the specified MetaProperty host.
        /// </summary>
        /// <param name="metaPropertyHost">The MetaProperty host.</param>
        /// <param name="metaProperty">The MetaProperty.</param>
        public static void Initialize(this IMetaPropertyHost metaPropertyHost, IMetaProperty metaProperty)
        {
            Initialize(metaPropertyHost, metaProperty, null, (IEnumerable<Attribute>)null);
        }

        /// <summary>
        /// Initializes the specified MetaProperty host.
        /// </summary>
        /// <param name="metaPropertyHost">The MetaProperty host.</param>
        /// <param name="metaProperty">The MetaProperty.</param>
        /// <param name="propertyName">Name of the property.</param>
        public static void Initialize(this IMetaPropertyHost metaPropertyHost, IMetaProperty metaProperty, string propertyName)
        {
            Initialize(metaPropertyHost, metaProperty, propertyName, (IEnumerable<Attribute>)null);
        }

        /// <summary>
        /// Initializes the specified MetaProperty host.
        /// </summary>
        /// <param name="metaPropertyHost">The MetaProperty host.</param>
        /// <param name="metaProperty">The MetaProperty.</param>
        /// <param name="attributes">The attributes.</param>
        public static void Initialize(this IMetaPropertyHost metaPropertyHost, IMetaProperty metaProperty, params Attribute[] attributes)
        {
            Initialize(metaPropertyHost, metaProperty, null, (IEnumerable<Attribute>)attributes);
        }

        /// <summary>
        /// Initializes the specified MetaProperty host.
        /// </summary>
        /// <param name="metaPropertyHost">The MetaProperty host.</param>
        /// <param name="metaProperty">The MetaProperty.</param>
        /// <param name="attributes">The attributes.</param>
        public static void Initialize(this IMetaPropertyHost metaPropertyHost, IMetaProperty metaProperty, IEnumerable<Attribute> attributes)
        {
            Initialize(metaPropertyHost, metaProperty, null, (IEnumerable<Attribute>)attributes);
        }

        /// <summary>
        /// Initializes the specified MetaProperty host.
        /// </summary>
        /// <param name="metaPropertyHost">The MetaProperty host.</param>
        /// <param name="metaProperty">The MetaProperty.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="attributes">The attributes.</param>
        public static void Initialize(this IMetaPropertyHost metaPropertyHost, IMetaProperty metaProperty, string propertyName, params Attribute[] attributes)
        {
            Initialize(metaPropertyHost, metaProperty, propertyName, (IEnumerable<Attribute>)attributes);
        }

        /// <summary>
        /// Initializes the specified MetaProperty host.
        /// </summary>
        /// <param name="metaPropertyHost">The MetaProperty host.</param>
        /// <param name="metaProperty">The MetaProperty.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="attributes">The attributes.</param>
        public static void Initialize(this IMetaPropertyHost metaPropertyHost, IMetaProperty metaProperty, string propertyName, IEnumerable<Attribute> attributes)
        {
            if (metaPropertyHost == null)
            {
                throw new ArgumentNullException("metaPropertyHost");
            }

            if (metaProperty == null)
            {
                throw new ArgumentNullException("metaProperty");
            }

            if (propertyName == null)
            {
                propertyName = Guid.NewGuid().ToString();
            }

            var info = new MetaPropertyConfiguration(
                metaPropertyHost,
                propertyName,
                attributes);

            metaProperty.Initialize(info);
        }

        /// <summary>
        /// Gets the synchronization context container from a set of attributes.
        /// </summary>
        /// <param name="attributes">The attributes.</param>
        /// <returns>The synchronization context container.</returns>
        private static ISynchronizationContextContainer GetSynchronizationContextContainerFromAttributes(IEnumerable<Attribute> attributes)
        {
            SetSynchronizationContextContainerAttribute attribute = attributes
                            .OfType<SetSynchronizationContextContainerAttribute>()
                            .FirstOrDefault();

            if (attribute == null)
            {
                return null;
            }
            else
            {
                if (attribute.ContainerType == null)
                {
                    throw new InvalidOperationException("No synchronization context container type specified.");
                }

                if (!typeof(ISynchronizationContextContainer).IsAssignableFrom(attribute.ContainerType))
                {
                    throw new InvalidOperationException("The specified synchronization context container type does not implement " + typeof(ISynchronizationContextContainer).Name + ".");
                }

                return (ISynchronizationContextContainer)Activator.CreateInstance(attribute.ContainerType);
            }
        }

        ////public static void InitializePublic(this MetaPropertyHost host, IMetaProperty metaProperty)
        ////{
        ////    InitializePublic(host, metaProperty, null, (IEnumerable<Attribute>)null);
        ////}

        ////public static void InitializePublic(this MetaPropertyHost host, IMetaProperty metaProperty, string name)
        ////{
        ////    InitializePublic(host, metaProperty, name, (IEnumerable<Attribute>)null);
        ////}

        ////public static void InitializePublic(this MetaPropertyHost host, IMetaProperty metaProperty, params Attribute[] attributes)
        ////{
        ////    InitializePublic(host, metaProperty, null, (IEnumerable<Attribute>)attributes);
        ////}

        ////public static void InitializePublic(this MetaPropertyHost host, IMetaProperty metaProperty, IEnumerable<Attribute> attributes)
        ////{
        ////    InitializePublic(host, metaProperty, null, (IEnumerable<Attribute>)attributes);
        ////}

        ////public static void InitializePublic(this MetaPropertyHost host, IMetaProperty metaProperty, string name, params Attribute[] attributes)
        ////{
        ////    InitializePublic(host, metaProperty, name, (IEnumerable<Attribute>)attributes);
        ////}

        ////public static void InitializePublic(this MetaPropertyHost host, IMetaProperty metaProperty, string name, IEnumerable<Attribute> attributes)
        ////{
        ////    Initialize(host, metaProperty, name, attributes);
        ////    metaProperty.PropertyChanged += (s, e) => host.OnPropertyChanged(e.PropertyName);
        ////}

        ////public static IMetaProperty<TValue> InitializeAdapted<TSource, TValue>(
        ////    this IMetaPropertyHost host,
        ////    TSource source,
        ////    string propertyName,
        ////    Func<TSource, TValue> getValueMethod)

        ////    where TSource : INotifyPropertyChanged
        ////{
        ////    var adapter = new PropertyChangedAdapter<TSource, TValue>(source, propertyName, getValueMethod);
        ////    IMetaProperty<TValue> metaProperty = new MetaProperty<TValue>(adapter);
        ////    host.Initialize(metaProperty);
        ////    return metaProperty;
        ////}

        ////public static IMetaProperty<TValue> InitializePublicAdapted<TSource, TValue>(
        ////    this MetaPropertyHost host,
        ////    TSource source,
        ////    string propertyName,
        ////    Func<TSource, TValue> getValueMethod)

        ////    where TSource : INotifyPropertyChanged
        ////{
        ////    var metaProperty = host.InitializeAdapted(source, propertyName, getValueMethod);
        ////    metaProperty.PropertyChanged += (s, e) => host.OnPropertyChanged(e.PropertyName);
        ////    return metaProperty;
        ////}
    }
}
